#include "HttpResponse.h"
#include "./common/YXConstant.h"
#include "Http.h"
#include "HttpClient.h"
#include "Server.h"

#include <errno.h>
#include <string.h>
#include <unistd.h>

#include "common/HashTable.h"

#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#elif defined(__linux__)
#include <poll.h>
#include <sys/socket.h>
#else
#error "not support platform"
#endif

static char *httpStatusMap[600];


__attribute__((constructor(101))) static void httpStatusMapInit() {
    httpStatusMap[HTTP_OK]                            = HTTP_STATUS_CODE_200;
    httpStatusMap[HTTP_CREATED]                       = HTTP_STATUS_CODE_201;
    httpStatusMap[HTTP_ACCEPTED]                      = HTTP_STATUS_CODE_202;
    httpStatusMap[HTTP_NO_CONTENT]                    = HTTP_STATUS_CODE_204;
    httpStatusMap[HTTP_RESET_CONTENT]                 = HTTP_STATUS_CODE_205;
    httpStatusMap[HTTP_PARTIAL_CONTENT]               = HTTP_STATUS_CODE_206;
    httpStatusMap[HTTP_MULTIPLE_CHOICES]              = HTTP_STATUS_CODE_300;
    httpStatusMap[HTTP_MOVED_PERMANENTLY]             = HTTP_STATUS_CODE_301;
    httpStatusMap[HTTP_FOUND]                         = HTTP_STATUS_CODE_302;
    httpStatusMap[HTTP_SEE_OTHER]                     = HTTP_STATUS_CODE_303;
    httpStatusMap[HTTP_NOT_MODIFIED]                  = HTTP_STATUS_CODE_304;
    httpStatusMap[HTTP_USE_PROXY]                     = HTTP_STATUS_CODE_305;
    httpStatusMap[HTTP_TEMPORARY_REDIRECT]            = HTTP_STATUS_CODE_307;
    httpStatusMap[HTTP_BAD_REQUEST]                   = HTTP_STATUS_CODE_400;
    httpStatusMap[HTTP_UNAUTHORIZED]                  = HTTP_STATUS_CODE_401;
    httpStatusMap[HTTP_PAYMENT_REQUIRED]              = HTTP_STATUS_CODE_402;
    httpStatusMap[HTTP_FORBIDDEN]                     = HTTP_STATUS_CODE_403;
    httpStatusMap[HTTP_NOT_FOUND]                     = HTTP_STATUS_CODE_404;
    httpStatusMap[HTTP_METHOD_NOT_ALLOWED]            = HTTP_STATUS_CODE_405;
    httpStatusMap[HTTP_NOT_ACCEPTABLE]                = HTTP_STATUS_CODE_406;
    httpStatusMap[HTTP_PROXY_AUTHENTICATION_REQUIRED] = HTTP_STATUS_CODE_407;
    httpStatusMap[HTTP_REQUEST_TIMEOUT]               = HTTP_STATUS_CODE_408;
    httpStatusMap[HTTP_CONFLICT]                      = HTTP_STATUS_CODE_409;
    httpStatusMap[HTTP_GONE]                          = HTTP_STATUS_CODE_410;
    httpStatusMap[HTTP_LENGTH_REQUIRED]               = HTTP_STATUS_CODE_411;
    httpStatusMap[HTTP_PRECONDITION_FAILED]           = HTTP_STATUS_CODE_412;
    httpStatusMap[HTTP_PAYLOAD_TOO_LARGE]             = HTTP_STATUS_CODE_413;
    httpStatusMap[HTTP_URI_TOO_LONG]                  = HTTP_STATUS_CODE_414;
    httpStatusMap[HTTP_UNSUPPORTED_MEDIA_TYPE]        = HTTP_STATUS_CODE_415;
    httpStatusMap[HTTP_RANGE_NOT_SATISFIABLE]         = HTTP_STATUS_CODE_416;
    httpStatusMap[HTTP_EXPECTATION_FAILED]            = HTTP_STATUS_CODE_417;
    httpStatusMap[HTTP_IM_A_TEAPOT]                   = HTTP_STATUS_CODE_418;
    httpStatusMap[HTTP_INTERNAL_SERVER_ERROR]         = HTTP_STATUS_CODE_500;
    httpStatusMap[HTTP_NOT_IMPLEMENTED]               = HTTP_STATUS_CODE_501;
    httpStatusMap[HTTP_BAD_GATEWAY]                   = HTTP_STATUS_CODE_502;
    httpStatusMap[HTTP_SERVICE_UNAVAILABLE]           = HTTP_STATUS_CODE_503;
    httpStatusMap[HTTP_GATEWAY_TIMEOUT]               = HTTP_STATUS_CODE_504;
    httpStatusMap[HTTP_HTTP_VERSION_NOT_SUPPORTED]    = HTTP_STATUS_CODE_505;
}

Response *create_response() {
    Response *response           = malloc(sizeof(Response));
    response->type               = HTTP_RESPONSE_SYNC;
    response->callback           = NULL;
    response->staus              = 200;
    response->staus_title        = HTTP_STATUS_CODE_200;
    response->headers            = linked_list_create();
    response->headers->freeValue = free;
    response->content_type       = MIME_TYPE_JSON;
    response->content_length     = -1;
    response->body               = sds_create_empty(HTTP_RESPONSE_BODY_BUFFER_INIT_SIZE);
    response->bodypos            = 0;

    return response;
}

void reset_response(Response *response) {
    response->type        = HTTP_RESPONSE_SYNC;
    response->callback    = NULL;
    response->staus       = 200;
    response->staus_title = HTTP_STATUS_CODE_200;
    linked_list_reset(response->headers);
    response->content_type   = MIME_TYPE_JSON;
    response->content_length = -1;
    sds_clear(response->body);
    response->bodypos = 0;
}

void free_response(Response *response) {
    linked_list_free(response->headers);
    sds_free(response->body);
    free(response);
}

void set_response_status(Response *response, int status) {
    response->staus = status;
    response->staus_title = httpStatusMap[status];
}

void set_response_header(Response *response, char *key, char *value) {
    Header *header = malloc(sizeof(Header));
    header->key    = key;
    header->value  = value;
    linked_list_add_r(response->headers, header);
}

void set_response_body(Response *response, char *body) { sds_cat_len(response->body, body, strlen(body)); }

void set_response_content_type(Response *response, char *content_type) { response->content_type = content_type; }

int do_response(EventLoop *el, Client *client) {
    // 构建响应报文
    generate_response(client);
    net_create_file_event(el, client->fd, YX_NET_WRITABLE, send_response, client);
    return YX_OK;
}

void generate_response(Client *client) {
    Response *response = client->response;

    int n;
    sds buf = client->input_buffer;
    sds_clear(buf);
    // 写入状态行
    sds_cat_printf(buf, "HTTP/1.1 %d %s\r\n", response->staus, response->staus_title);
    // 写入响应头
    sds_cat_len(buf, "Server: MT/0.0.1\r\nConnection: keep-alive\r\n", 42);
    sds_cat_printf(buf, "Content-Type: %s\r\n", response->content_type);

    if (response->content_length == -1) {
        response->content_length = sds_len(response->body);
    }
    n = sprintf(buf + sds_len(buf), "Content-Length: %d\r\n", response->content_length);
    sds_incr_len(buf, n);

    // 控制层设置的响应头
    LinkedListIterator *iterator = linked_list_iterator_create(response->headers);
    if (iterator) {
        Node   *node   = NULL;
        Header *header = NULL;
        while ((node = linked_list_iterator_next(iterator)) != NULL) {
            header = (Header *) node->value;
            n      = sprintf(buf + sds_len(buf), "%s: %s\r\n", header->key, header->value);
            sds_incr_len(buf, n);
        }
        linked_list_iterator_free(iterator);
    }

    sds_cat_len(buf, "\r\n", 2);
}


void set_async_response(Response *resp, int (*callback)(Client *response)) {
    resp->type     = HTTP_RESPONSE_ASYNC;
    resp->callback = callback;
}

int async_response_callback(Client *client) {
    Request  *request  = client->request;
    Response *response = client->response;

    char *full_path = malloc(HTTP_REQUEST_FULL_PATH_MAX_LENGTH);
    sprintf(full_path, "%s%s", FILE_RESOURCE_PATH_PREFIX, request->path);

    FILE *fp = fopen(full_path, "rb");
    free(full_path);

    if (fp == NULL) {
        return YX_ERR;
    }

    struct pollfd pollfds = {client->fd, POLLOUT};
    // 读取文件内容
    int n;
    while ((n = fread(response->body + sds_len(response->body), 1, sds_free_len(response->body), fp)) > 0) {
        sds_incr_len(response->body, n);
        response->bodypos = 0;
        while (sds_len(response->body)) {
#if defined(_WIN32) || defined(WIN64)
            WSAPoll(&pollfds, 1, 2);
#elif defined(__linux__)
            poll(&pollfds, 1, 2);
#else
#error unsupported platform
#endif
            n = send(client->fd, response->body + response->bodypos, sds_len(response->body), 0);
            if (n == 0) {
                fclose(fp);
                return YX_ERR;
            }
            if (n == -1) {
                if (errno != EAGAIN) {
                    fclose(fp);
                    return YX_ERR;
                }
                continue;
            }
            response->bodypos += n;
            sds_update_len(response->body, sds_len(response->body) - n);
        }
    }
    // 关闭文件
    fclose(fp);

    return YX_OK;
}

void async_send_response(Client *client) {
    Response *response = client->response;
    if (response->callback(client) == YX_ERR) {
        set_client_is_closed(client);
        return;
    }
    reset_client(client);
}